package knapsackProblem;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Random;
import java.util.Set;
import java.util.Vector;

import javax.swing.InputVerifier;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JTable;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableColumn;
import javax.swing.text.JTextComponent;


import org.joda.time.Duration;
import org.joda.time.Instant;


import execute.InteractiveTableModel;


@SuppressWarnings("serial")
public class Frame extends execute.Frame {
	protected InteractiveTableModel tblModel;

	/** Creates new form Frame */
	public Frame() {
		super.initComponents();
		initComponents();
	}

	final private String strMinLoadTime = "Minimal loading time",
			strMaxLoadTime = "Maximal loading time",
			strMinItemVal = "Minimal item value",
			strMaxItemVal = "Maximal item value",
			strHowManyItems = "How many items?",
			strMaxKnapsackLoadTime = "Maximal loading time",
			strHowManyShippers = "How many shippers?"
			;

	private static String strShouldBeEnteredNumber(int greaterThan, int lessThan) {
		return "Should be entered number:\n greater than " + greaterThan
				+ ",\n less than " + lessThan;
	}

	public void highlightLastRow(int row) {
		int lastrow = tblModel.getRowCount();
		if (row == lastrow - 1) {
			tblKnapsackItems.setRowSelectionInterval(lastrow - 1, lastrow - 1);
		} else {
			tblKnapsackItems.setRowSelectionInterval(row + 1, row + 1);
		}

		tblKnapsackItems.setColumnSelectionInterval(0, 0);
	}

	abstract class AbstractVerifier extends InputVerifier {
		private int greaterThan, lessThan;

		public AbstractVerifier(int greaterThan, int lessThan) {
			this.greaterThan = greaterThan;
			this.lessThan = lessThan;
		}

		public boolean verify(JComponent input) {
			final JTextComponent source = (JTextComponent) input;
			try {
				Integer number = Integer.parseInt(source.getText());
				if (number <= greaterThan || number >= lessThan)
					throw new NumberFormatException();
			} catch (NumberFormatException e) {
				btnGenerateValues.setEnabled(false);
				JOptionPane.showMessageDialog(source, Frame
						.strShouldBeEnteredNumber(greaterThan, lessThan),
						"Error Dialog", JOptionPane.ERROR_MESSAGE);
				return false;
			}

			additionalVerification();
			return true;
		}

		protected abstract void additionalVerification();
	}

	private void initNumberField(final javax.swing.JTextField tf,
			String initText, AbstractVerifier verifier)
	{
		this.initNumberField(tf, initText,"", verifier);
	}
	
	private void initNumberField(final javax.swing.JTextField tf,
			String initText,int defaultValue, AbstractVerifier verifier) {
		this.initNumberField(tf, initText,Integer.toString(defaultValue) , verifier);
	}
	
	private void initNumberField(final javax.swing.JTextField tf,
			String initText,final String defaultValue, AbstractVerifier verifier) {
		tf.setText(initText);
		tf.setToolTipText(initText+" -- "+strShouldBeEnteredNumber(verifier.greaterThan, verifier.lessThan));
		tf.setInputVerifier(verifier);
		tf.addFocusListener(new java.awt.event.FocusAdapter() {
			public void focusGained(java.awt.event.FocusEvent evt) {
				tf.setText(defaultValue);
			}
		});
	}

	private void initNumberFieldInGenerateValuePane(
			final javax.swing.JTextField tf, String initText,
			final int greaterThan, final int lessThan) {

		AbstractVerifier verifier = new AbstractVerifier(greaterThan, lessThan) {
			@Override
			protected void additionalVerification() {
				if (!tfMinItemVal.getText().equals(strMinItemVal)
						&& !tfMaxItemVal.getText().equals(strMaxItemVal)
						&& !tfMinLoadTime.getText().equals(strMinLoadTime)
						&& !tfMaxLoadTime.getText().equals(strMaxLoadTime)
						&& !tfHowManyItems.getText().equals(strHowManyItems))
					btnGenerateValues.setEnabled(true);
			}
		};

		this.initNumberField(tf, initText, verifier);
	}
	
	private void initNumberFieldInSimulatePane(
			final javax.swing.JTextField tf, String initText,
			final int greaterThan, final int lessThan) {
		this.initNumberFieldInSimulatePane(
				tf,  initText,Integer.MIN_VALUE,
				 greaterThan, lessThan);
	}
	
	private void initNumberFieldInSimulatePane(
			final javax.swing.JTextField tf, String initText,int defaultValue,
			final int greaterThan, final int lessThan) {

		AbstractVerifier verifier = new AbstractVerifier(greaterThan, lessThan) {
			@Override
			protected void additionalVerification() {
				if (!tfMaxKnapsackLoadTime.getText().equals(
						strMaxKnapsackLoadTime)
						&&
						!tfHowManyShippers.getText().equals(strHowManyShippers))
					btnSimulate.setEnabled(true);
			}
			};
			if (defaultValue > greaterThan && defaultValue < lessThan)
		this.initNumberField(tf, initText,defaultValue, verifier);
			else
				this.initNumberField(tf, initText, verifier);
	}

	public class InteractiveTableModelListener implements TableModelListener {
		public void tableChanged(TableModelEvent evt) {
			if (evt.getType() == TableModelEvent.UPDATE) {
				int column = evt.getColumn();
				int row = evt.getFirstRow();
				tblKnapsackItems.setColumnSelectionInterval(column + 1,
						column + 1);
				tblKnapsackItems.setRowSelectionInterval(row, row);
			}
		}
	}

	class InteractiveRenderer extends DefaultTableCellRenderer {
		protected int interactiveColumn;

		public InteractiveRenderer(int interactiveColumn) {
			this.interactiveColumn = interactiveColumn;
		}

		public Component getTableCellRendererComponent(JTable table,
				Object value, boolean isSelected, boolean hasFocus, int row,
				int column) {
			Component c = super.getTableCellRendererComponent(table, value,
					isSelected, hasFocus, row, column);
			if (column == interactiveColumn && hasFocus) {
				if ((Frame.this.tblModel.getRowCount() - 1) == row
						&& !Frame.this.tblModel.hasEmptyRow()) {
					Frame.this.tblModel.addEmptyRow();
				}

				highlightLastRow(row);
			}

			return c;
		}
	}

	protected void initComponents() {
		final String[] columnNames = { "Item ID", "Loading time", "Item value",
				"" };
		tblModel = new InteractiveTableModel(columnNames);
		tblModel.addTableModelListener(new Frame.InteractiveTableModelListener());
		tblKnapsackItems.setModel(tblModel);
		tblKnapsackItems.setSurrendersFocusOnKeystroke(true);
		if (!tblModel.hasEmptyRow()) {
			tblModel.addEmptyRow();
		}
		TableColumn hidden = tblKnapsackItems.getColumnModel().getColumn(
				InteractiveTableModel.HIDDEN_INDEX);
		hidden.setMinWidth(2);
		hidden.setPreferredWidth(2);
		hidden.setMaxWidth(2);
		hidden.setCellRenderer(new InteractiveRenderer(
				InteractiveTableModel.HIDDEN_INDEX));

		initNumberFieldInGenerateValuePane(tfMinLoadTime, strMinLoadTime, 0, 10);
		initNumberFieldInGenerateValuePane(tfMaxLoadTime, strMaxLoadTime, 0, 10);
		initNumberFieldInGenerateValuePane(tfMinItemVal, strMinItemVal, 0, 10);
		initNumberFieldInGenerateValuePane(tfMaxItemVal, strMaxItemVal, 0, 10);
		initNumberFieldInGenerateValuePane(tfHowManyItems, strHowManyItems, 0,
				10);
		initNumberFieldInSimulatePane(tfMaxKnapsackLoadTime, strMaxKnapsackLoadTime,
				10, 100);
		initNumberFieldInSimulatePane(tfHowManyShippers,strHowManyShippers,1,0,10);

		btnGenerateValues.setEnabled(false);
		btnGenerateValues.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {

				int minIV = Math.min(Integer.parseInt(tfMinItemVal.getText()),
						Integer.parseInt(tfMaxItemVal.getText()));
				int maxIV = Math.max(Integer.parseInt(tfMinItemVal.getText()),
						Integer.parseInt(tfMaxItemVal.getText()));
				int dIV = maxIV - minIV;

				int minLT = Math.min(Integer.parseInt(tfMinLoadTime.getText()),
						Integer.parseInt(tfMaxLoadTime.getText()));
				int maxLT = Math.max(Integer.parseInt(tfMinLoadTime.getText()),
						Integer.parseInt(tfMaxLoadTime.getText()));
				int dLT = maxLT - minLT;

				int howManyGenerate = Integer.parseInt(tfHowManyItems.getText());
				List<Item> items = new ArrayList<Item>(howManyGenerate);

				Random randomizer = new Random();
				for (int i = 0; i < howManyGenerate; i++) {
					int IV = randomizer.nextInt(dIV) + minIV;
					int LT = randomizer.nextInt(dLT) + minLT;

					Item item = new Item(IV, (long) LT);
					items.add(item);
				}

				tblModel.addRows(items);
			}
		});

		btnSimulate.setEnabled(false);
		btnSimulate.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent arg0) {
		        
		        long start = System.currentTimeMillis();
				Vector<KnapsackItem> dataVector = tblModel.getDataVector();
				Set<Task> tasks = new HashSet<Task>(dataVector
						.size());
				
				Set<KnapsackItem> ki  = new HashSet<KnapsackItem>(dataVector
						.size());
				
				for (int i = 0; i < dataVector.size(); i++) {
					KnapsackItem k = dataVector.get(i);
					ki.add(k);
					Task t = new Task(k, new Balance(k.getProfit(), 0), new Duration(k.getTime()));
					tasks.add(t);
				}							
				
				int restrictions = Integer.parseInt(tfMaxKnapsackLoadTime
						.getText());
				
				initKnapsack(ki, restrictions);
				
				try {
					knapsackProblem.Executor.run(tasks,new Instant(0),new Instant(restrictions));
				} catch (Exception e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				
				long end = System.currentTimeMillis();
				System.out.println(end- start);
			}
		});
	}
	
    public static void initKnapsack(Set<KnapsackItem> itemsL, int restrictions) {

        Knapsack k = new Knapsack(restrictions);

        List<KnapsackItem> previousStates = new ArrayList<KnapsackItem>();
        Set<KnapsackItem> notAdmissibleStates = new HashSet<KnapsackItem>();

        execute.ShowIt showIt = new execute.ShowIt();
        KnapsackItem nextState = new KnapsackItem(-1, 0, 0);
        List<KnapsackItem> goalStates;
        showIt.GenerateRoot(nextState);
        do {
                KnapsackItem previousState = nextState;
                goalStates = k.getGoalStates(previousStates, itemsL,
                                notAdmissibleStates);

                if (goalStates.size() > 0) {
                        Collections.sort(goalStates);
                        nextState = goalStates.get(0);
                        previousStates.add(nextState);

                        showIt.AddChildren(previousState, goalStates);
                }
        } while (goalStates.size() > 0);

        showIt.pack();
        showIt.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
        showIt.setSize(400, 320);
        showIt.setVisible(true);
}
}
